package org.liaohailong.library.widget.refresh

import android.content.Context
import android.graphics.PointF
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewConfiguration
import android.view.ViewGroup
import android.widget.Scroller

/**
 * Author: liaohailong
 * Date: 2019/4/25
 * Time: 20:13
 * Description: 下拉刷新
 **/
class RefreshLayout : ViewGroup {
    constructor(context: Context, attributeSet: AttributeSet?, defStyle: Int) : super(context, attributeSet, defStyle)
    constructor(context: Context, attributeSet: AttributeSet?) : this(context, attributeSet, 0)
    constructor(context: Context) : this(context, null)

    private val oneScrollDistance = ViewConfiguration.get(context).scaledTouchSlop //最大滑动距离，防止滑动距离过大时掉帧
    private var scroller: Scroller = Scroller(context)
    private var status: Status = Status.IDLE
    private lateinit var headerView: View
    private lateinit var contentView: View
    var onRefreshHandler: OnRefreshHandler? = null
    private var maxScrollDistance: Int = 0

    override fun onFinishInflate() {
        super.onFinishInflate()
        headerView = getChildAt(0)
        contentView = getChildAt(1)
        if (childCount > 2) IllegalStateException("you mush put two views, header view and content view!")

    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        for (i in 0 until childCount) {
            val view = getChildAt(i)
            measureChild(view, widthMeasureSpec, heightMeasureSpec)
        }
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        maxScrollDistance = h
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        val headerView = getChildAt(0)
        val contentView = getChildAt(1)

        headerView.layout(l, -headerView.measuredHeight, r, 0)
        contentView.layout(l, t, r, b)
    }

    private val touchPoint = PointF()
    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        onRefreshHandler?.apply {
            val refreshHeaderView = headerView as IRefreshHeaderView
            ev?.apply {
                when (action) {
                    MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN -> {
                        if (!scroller.isFinished) {
                            scroller.forceFinished(true)
                        }
                        touchPoint.set(getX(0), getY(0))
                        when (status) {
                            Status.IDLE -> {
                                refreshHeaderView.onPullBegin()
                            }
                            Status.REFRESH_BEGIN -> {
                                //do nothing...
                            }
                            Status.REFRESH_END -> {
                                //do nothing...
                            }
                        }
                    }
                    MotionEvent.ACTION_MOVE -> {
                        var yOffset = y - touchPoint.y
                        yOffset = if (yOffset > 0) {
                            Math.min(oneScrollDistance.toFloat(), yOffset)
                        } else {
                            Math.max(-oneScrollDistance.toFloat(), yOffset)
                        }
                        val refreshing = handlerPullRefresh()
                        /*
                        * 拦截条件
                        * 1，正在下拉并且外部希望拦截
                        * 2，scrollY < 0 当前处于下拉状态
                        * */
                        val interrupt = (yOffset > 0 && refreshing) || scrollY < 0

                        if (interrupt) {
                            when (status) {
                                Status.IDLE -> {
                                    var tempY = Math.round(scrollY - yOffset)
                                    tempY = Math.max(-maxScrollDistance, tempY)
                                    tempY = Math.min(0, tempY)
                                    scrollTo(scrollX, tempY)
                                    var progress = Math.abs(tempY).toFloat() / Math.abs(headerView.measuredHeight).toFloat()
                                    progress = Math.min(1f, progress)
                                    onRefreshHandler?.onRefreshProgress(progress)
                                    refreshHeaderView.onPullProgress(progress)
                                }
                                Status.REFRESH_BEGIN -> {
                                    //do nothing...
                                }
                                Status.REFRESH_END -> {
                                    //do nothing...
                                }
                            }
                        }
                        touchPoint.set(getX(0), getY(0))

                        if (interrupt) {
                            return true
                        }
                    }
                    MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
                        val scrollX = scrollX
                        val scrollY = scrollY
                        //下拉距离达标
                        if (Math.abs(scrollY) >= headerView.measuredHeight) {
                            status = Status.REFRESH_BEGIN
                            //滑动->全部显示headerView
                            val dy = headerView.measuredHeight + scrollY
                            scroller.startScroll(scrollX, scrollY, 0, -dy)
                            onRefreshHandler?.onRefreshBegin()
                            refreshHeaderView.onRefreshBegin()
                        }
                        //下拉距离过小，放弃刷新
                        else {
                            status = Status.IDLE
                            scroller.startScroll(scrollX, scrollY, 0, -scrollY)
                        }
                        invalidate()
                    }
                }
            }
        }
        return super.dispatchTouchEvent(ev)
    }

    override fun computeScroll() {
        if (scroller.computeScrollOffset()) {
            val currX = scroller.currX
            val currY = scroller.currY
            scrollTo(currX, currY)
            //刷新状态恢复后，返回缩回时的进度
            val refreshHeaderView = headerView as IRefreshHeaderView
            var progress = Math.abs(currY).toFloat() / Math.abs(headerView.measuredHeight).toFloat()
            progress = Math.min(1f, progress)
            onRefreshHandler?.onRefreshProgress(progress)
            refreshHeaderView.onPullProgress(progress)

            touchPoint.set(currX.toFloat(), currY.toFloat())
            invalidate()
        } else {
            val refreshHeaderView = headerView as IRefreshHeaderView
            when (status) {
                Status.IDLE -> {
                    //do nothing...
                }
                Status.REFRESH_BEGIN -> {
                    //do nothing...
                }
                Status.REFRESH_END -> {
                    status = Status.IDLE
                    onRefreshHandler?.onRefreshEnd()
                    refreshHeaderView.onRefreshEnd()
                }
            }
        }
    }

    fun stopRefresh() {
        if (status == Status.REFRESH_BEGIN) {
            status = Status.REFRESH_END

            val scrollX = scrollX
            val scrollY = scrollY
            scroller.startScroll(scrollX, scrollY, 0, -scrollY)
            invalidate()
        }
    }

    enum class Status {
        IDLE,//未下拉刷新，空闲状态
        REFRESH_BEGIN,//开始刷新
        REFRESH_END//结束刷新
    }

    interface OnRefreshHandler {
        /**
         * 是否拦截开始刷新
         */
        fun handlerPullRefresh(): Boolean

        /**
         * 松手后开始刷新
         */
        fun onRefreshBegin()

        /**
         * 刷新开始/结束过程中的动画进度
         * @param progress [0.0f ~ 1.0f]
         */
        fun onRefreshProgress(progress: Float)

        /**
         * 刷新结束
         */
        fun onRefreshEnd()
    }
}